/*
 * Tigase Jabber/XMPP Server
 * Copyright (C) 2004-2012 "Artur Hefczyc" <artur.hefczyc@tigase.org>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. Look for COPYING file in the top folder.
 * If not, see http://www.gnu.org/licenses/.
 *
 * $Rev: 2996 $
 * Last modified by $Author: wojtek $
 * $Date: 2012-08-21 06:29:57 +0800 (Tue, 21 Aug 2012) $
 */

package tigase.server;

import tigase.conf.Configurable;

import tigase.disco.ServiceEntity;
import tigase.disco.ServiceIdentity;
import tigase.disco.XMPPService;

import tigase.server.script.AddScriptCommand;
import tigase.server.script.CommandIfc;
import tigase.server.script.RemoveScriptCommand;

import tigase.util.DNSResolver;
import tigase.util.TigaseStringprepException;

import tigase.vhosts.VHostItem;
import tigase.vhosts.VHostListener;
import tigase.vhosts.VHostManagerIfc;

import tigase.xml.Element;

import tigase.xmpp.Authorization;
import tigase.xmpp.BareJID;
import tigase.xmpp.JID;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;

import java.util.Arrays;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.script.Bindings;
import javax.script.ScriptEngineManager;

/**
 * Created: Oct 17, 2009 7:49:05 PM
 * 
 * @author <a href="mailto:artur.hefczyc@tigase.org">Artur Hefczyc</a>
 * @version $Rev: 2996 $
 */
public class BasicComponent implements Configurable, XMPPService, VHostListener {

	/** Field description */
	public static final String SCRIPTS_DIR_PROP_DEF = "scripts/admin";

	/** Field description */
	public static final String SCRIPTS_DIR_PROP_KEY = "scripts-dir";

	/** Field description */
	public static final String COMMAND_PROP_NODE = "command";

	/** Field description */
	public static final String ALL_PROP_KEY = "ALL";

	/**
	 * Variable <code>log</code> is a class logger.
	 */
	private static final Logger log = Logger.getLogger(BasicComponent.class.getName());

	private String DEF_HOSTNAME_PROP_VAL = DNSResolver.getDefaultHostname();
	private JID compId = null;
	private String name = null;
	private BareJID defHostname = BareJID.bareJIDInstanceNS(DEF_HOSTNAME_PROP_VAL);
	protected Map<String, CommandIfc> scriptCommands =
			new ConcurrentHashMap<String, CommandIfc>(20);
	private boolean nonAdminCommands = false;
	private Map<String, EnumSet<CmdAcl>> commandsACL =
			new ConcurrentHashMap<String, EnumSet<CmdAcl>>(20);

	/**
	 * List of the component administrators
	 */
	protected Set<BareJID> admins = new ConcurrentSkipListSet<BareJID>();
	private ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
	private String scriptsBaseDir = null;
	private String scriptsCompDir = null;
	private ServiceEntity serviceEntity = null;
	protected VHostManagerIfc vHostManager = null;

	/**
	 * Method description
	 * 
	 * 
	 * @param domain
	 */
	public void addComponentDomain(String domain) {
		vHostManager.addComponentDomain(domain);
	}

	/**
	 * 
	 * @param jid
	 * @param commandId
	 * @return
	 */
	public boolean canCallCommand(JID jid, String commandId) {
		boolean result = isAdmin(jid);

		if (result) {
			return true;
		}

		EnumSet<CmdAcl> acl = commandsACL.get(ALL_PROP_KEY);

		if (acl != null) {
			result = checkCommandAcl(jid, acl);
		}

		if (!result) {
			acl = commandsACL.get(commandId);

			if (acl != null) {
				result = checkCommandAcl(jid, acl);
			}
		}

		return result;
	}

	/**
	 * Method description
	 * 
	 * 
	 * @param jid
	 * @param acl
	 * 
	 * @return
	 */
	public boolean checkCommandAcl(JID jid, EnumSet<CmdAcl> acl) {
		for (CmdAcl cmdAcl : acl) {
			switch (cmdAcl) {
				case ALL:
					return true;

				case ADMIN:
					if (isAdmin(jid)) {
						return true;
					}

					break;

				case LOCAL:
					if (isLocalDomain(jid.getDomain())) {
						return true;
					}

					break;

				case DOMAIN:
					if (jid.getDomain().equals(cmdAcl.getAclVal())) {
						return true;
					}

					break;

				case JID:
				case OTHER:
				default:
					if (jid.getBareJID().toString().equals(cmdAcl.getAclVal())) {
						return true;
					}
			}
		}

		return false;
	}

	/**
	 * Method description
	 * 
	 * 
	 * @return
	 */
	@Override
	public JID getComponentId() {
		return compId;
	}

	/**
	 * Method description
	 * 
	 * 
	 * @return
	 */
	public BareJID getDefHostName() {
		return defHostname;
	}

	/**
	 * Method description
	 * 
	 * 
	 * @param params
	 * 
	 * @return
	 */
	@Override
	public Map<String, Object> getDefaults(Map<String, Object> params) {
		Map<String, Object> defs = new LinkedHashMap<String, Object>(50);

		defs.put(COMPONENT_ID_PROP_KEY, compId.toString());
		DEF_HOSTNAME_PROP_VAL = DNSResolver.getDefaultHostname();
		defs.put(DEF_HOSTNAME_PROP_KEY, DEF_HOSTNAME_PROP_VAL);

		String[] adm = null;

		if (params.get(GEN_ADMINS) != null) {
			adm = ((String) params.get(GEN_ADMINS)).split(",");
		} else {
			adm = new String[] { "admin@localhost" };
		}

		defs.put(ADMINS_PROP_KEY, adm);

		String scripts_dir = (String) params.get(GEN_SCRIPT_DIR);

		if (scripts_dir == null) {
			scripts_dir = SCRIPTS_DIR_PROP_DEF;
		}

		defs.put(SCRIPTS_DIR_PROP_KEY, scripts_dir);
		defs.put(COMMAND_PROP_NODE + "/" + ALL_PROP_KEY, CmdAcl.ADMIN.name());

		return defs;
	}

	/**
	 * Method description
	 * 
	 * 
	 * @return
	 */
	public String getDiscoCategoryType() {
		return "generic";
	}

	public String getDiscoCategory() {
		return "component";
	}

	/**
	 * Method description
	 * 
	 * 
	 * @return
	 */
	public String getDiscoDescription() {
		return "Undefined description";
	}

	/**
	 * Exists for backward compatibility with the old API.
	 * 
	 * @return
	 */
	@Deprecated
	public List<Element> getDiscoFeatures() {
		return null;
	}

	/**
	 * Method description
	 * 
	 * 
	 * @param from
	 * 
	 * @return
	 */
	@Override
	public List<Element> getDiscoFeatures(JID from) {
		return getDiscoFeatures();
	}

	/**
	 * Exists for backward compatibility with the old API.
	 * 
	 * @param node
	 * @param jid
	 * 
	 * @return
	 */
	@Deprecated
	public Element getDiscoInfo(String node, JID jid) {
		return null;
	}

	/**
	 * Method description
	 * 
	 * 
	 * @param node
	 * @param jid
	 * @param from
	 * 
	 * @return
	 */
	@Override
	public Element getDiscoInfo(String node, JID jid, JID from) {

		// This is only to support the old depreciated API.
		Element result = getDiscoInfo(node, jid);

		if (result != null) {
			return result;
		}

		// OLD API support end
		if (getName().equals(jid.getLocalpart())) {
			return serviceEntity.getDiscoInfo(node, isAdmin(from) || nonAdminCommands);
		}

		return null;
	}

	/**
	 * Exists for backward compatibility with the old API.
	 * 
	 * @deprecated
	 * 
	 * @param node
	 * @param jid
	 * 
	 * @return
	 */
	@Deprecated
	public List<Element> getDiscoItems(String node, JID jid) {
		return null;
	}

	public List<Element> getScriptItems(String node, JID jid, JID from) {
		LinkedList<Element> result = null;
		boolean isAdminFrom = isAdmin(from);
		if (node.equals("http://jabber.org/protocol/commands")
				&& (isAdminFrom || nonAdminCommands)) {
			result = new LinkedList<Element>();

			for (CommandIfc comm : scriptCommands.values()) {
				if (!comm.isAdminOnly() || isAdminFrom) {
					result
							.add(new Element("item", new String[] { "node", "name", "jid" },
									new String[] { comm.getCommandId(), comm.getDescription(),
											jid.toString() }));
				}
			}
		}
		return result;
	}

	/**
	 * Method description
	 * 
	 * 
	 * @param node
	 * @param jid
	 * @param from
	 * 
	 * @return
	 */
	@Override
	public List<Element> getDiscoItems(String node, JID jid, JID from) {

		// This is only to support the old depreciated API.
		List<Element> result = getDiscoItems(node, jid);

		if (result != null) {
			return result;
		}

		// OLD API support end
		boolean isAdminFrom = isAdmin(from);

		if (getName().equals(jid.getLocalpart())) {
			if (node != null) {
				result = getScriptItems(node, jid, from);
			} else {
				result =
						serviceEntity.getDiscoItems(null, jid.toString(), isAdminFrom
								|| nonAdminCommands);

				if (result != null) {
					for (Iterator<Element> it = result.iterator(); it.hasNext();) {
						Element element = it.next();

						if (element.getAttribute("node") == null) {
							it.remove();
						}
					}
				}
			}

			// Element result = serviceEntity.getDiscoItem(null, getName() + "." +
			// jid);
			if (log.isLoggable(Level.FINEST)) {
				log.log(Level.FINEST, "{0} Found disco items: {1}", new Object[] { getName(),
						((result != null) ? result.toString() : null) });
			}

			return result;
		} else {
			if (log.isLoggable(Level.FINEST)) {
				log.log(Level.FINEST, "{0} General disco items request, node: {1}", new Object[] {
						getName(), node });
			}

			if (node == null) {
				if (log.isLoggable(Level.FINEST)) {
					log.log(Level.FINEST, "{0} Disco items request for null node",
							new Object[] { getName() });
				}

				Element res = null;

				if (!serviceEntity.isAdminOnly() || isAdminFrom || nonAdminCommands) {
					res =
							serviceEntity.getDiscoItem(null,
									BareJID.toString(getName(), jid.toString()));

					if (log.isLoggable(Level.FINEST)) {
						log.log(Level.FINEST, "{0} not admin only or isAdmin, result: {1}",
								new Object[] { getName(), res });
					}
				}

				result = serviceEntity.getDiscoItems(null, null, isAdminFrom || nonAdminCommands);

				if (res != null) {
					if (result != null) {
						for (Iterator<Element> it = result.iterator(); it.hasNext();) {
							Element element = it.next();

							if (element.getAttribute("node") != null) {
								it.remove();
							}
						}

						result.add(0, res);
					} else {
						result = Arrays.asList(res);
					}
				}
			}

			return result;
		}
	}

	/**
	 * Method description
	 * 
	 * 
	 * @return
	 */
	@Override
	public String getName() {
		return name;
	}

	/**
	 * Method description
	 * 
	 * 
	 * @param domain
	 * 
	 * @return
	 */
	public VHostItem getVHostItem(String domain) {
		return (vHostManager != null) ? vHostManager.getVHostItem(domain) : null;
	}

	/**
	 * Method description
	 * 
	 * 
	 * @return
	 */
	@Override
	public boolean handlesLocalDomains() {
		return false;
	}

	/**
	 * Method description
	 * 
	 * 
	 * @return
	 */
	@Override
	public boolean handlesNameSubdomains() {
		return true;
	}

	/**
	 * Method description
	 * 
	 * 
	 * @return
	 */
	@Override
	public boolean handlesNonLocalDomains() {
		return false;
	}

	/**
	 * Method description
	 * 
	 * 
	 * @param binds
	 */
	public void initBindings(Bindings binds) {
		binds.put(CommandIfc.VHOST_MANAGER, vHostManager);
		binds.put(CommandIfc.ADMINS_SET, admins);
		binds.put(CommandIfc.COMMANDS_ACL, commandsACL);
		binds.put(CommandIfc.SCRI_MANA, scriptEngineManager);
		binds.put(CommandIfc.ADMN_CMDS, scriptCommands);
		binds.put(CommandIfc.ADMN_DISC, serviceEntity);
		binds.put(CommandIfc.SCRIPT_BASE_DIR, scriptsBaseDir);
		binds.put(CommandIfc.SCRIPT_COMP_DIR, scriptsCompDir);
		binds.put(CommandIfc.COMPONENT_NAME, getName());
	}

	/**
	 * Method description
	 * 
	 */
	@Override
	public void initializationCompleted() {
	}

	/**
	 * Method description
	 * 
	 * 
	 * @param jid
	 * 
	 * @return
	 */
	public boolean isAdmin(JID jid) {
		return admins.contains(jid.getBareJID());
	}

	/**
	 * Method description
	 * 
	 * 
	 * @param domain
	 * 
	 * @return
	 */
	public boolean isLocalDomain(String domain) {
		return (vHostManager != null) ? vHostManager.isLocalDomain(domain) : false;
	}

	/**
	 * Method description
	 * 
	 * 
	 * @param domain
	 * 
	 * @return
	 */
	public boolean isLocalDomainOrComponent(String domain) {
		return (vHostManager != null) ? vHostManager.isLocalDomainOrComponent(domain) : false;
	}

	/**
	 * Method description
	 * 
	 * 
	 * @param packet
	 * @param results
	 */
	@Override
	public void processPacket(Packet packet, Queue<Packet> results) {
		if (packet.isCommand() && getName().equals(packet.getStanzaTo().getLocalpart())
				&& isLocalDomain(packet.getStanzaTo().getDomain())) {
			if (log.isLoggable(Level.FINEST)) {
				log.log(Level.FINEST, "Command addressed to: {0}, command: {1}", new Object[] {
						getName(), packet });
			}
			processScriptCommand(packet, results);
		}
	}

	/**
	 * Method description
	 * 
	 */
	@Override
	public void release() {
	}

	/**
	 * Method description
	 * 
	 * 
	 * @param domain
	 */
	public void removeComponentDomain(String domain) {
		vHostManager.removeComponentDomain(domain);
	}

	/**
	 * Method description
	 * 
	 * 
	 * @param jid
	 * @param node
	 * @param description
	 */
	public void removeServiceDiscoveryItem(String jid, String node, String description) {
		ServiceEntity item = new ServiceEntity(jid, node, description);

		// item.addIdentities(new ServiceIdentity("component", identity_type,
		// name));
		if (log.isLoggable(Level.FINEST)) {
			log.log(Level.FINEST, "Modifying service-discovery info, removing: {0}", item);
		}

		serviceEntity.removeItems(item);
	}

	/**
	 * Method description
	 * 
	 * 
	 * @param name
	 */
	@Override
	public void setName(String name) {
		this.name = name;

		try {
			compId = JID.jidInstance(name, defHostname.getDomain(), null);
		} catch (TigaseStringprepException ex) {
			log.log(Level.WARNING, "Problem setting component ID: ", ex);
		}
	}

	/**
	 * Method description
	 * 
	 * 
	 * @param props
	 */
	@Override
	public void setProperties(Map<String, Object> props) {
		if (props.get(COMPONENT_ID_PROP_KEY) != null) {
			try {
				compId = JID.jidInstance((String) props.get(COMPONENT_ID_PROP_KEY));
			} catch (TigaseStringprepException ex) {
				log.log(Level.WARNING, "Problem setting component ID: ", ex);
			}
		}

		if (props.get(DEF_HOSTNAME_PROP_KEY) != null) {
			defHostname = BareJID.bareJIDInstanceNS((String) props.get(DEF_HOSTNAME_PROP_KEY));
		}

		String[] admins_tmp = (String[]) props.get(ADMINS_PROP_KEY);

		if (admins_tmp != null) {
			for (String admin : admins_tmp) {
				try {
					admins.add(BareJID.bareJIDInstance(admin));
				} catch (TigaseStringprepException ex) {
					log.log(Level.CONFIG, "Incorrect admin JID: ", ex);
				}
			}
		}

		for (Map.Entry<String, Object> entry : props.entrySet()) {
			if (entry.getKey().startsWith(COMMAND_PROP_NODE)) {
				String cmdId = entry.getKey().substring(COMMAND_PROP_NODE.length() + 1);
				String[] cmdAcl = entry.getValue().toString().split(",");
				EnumSet<CmdAcl> acl = EnumSet.noneOf(CmdAcl.class);

				for (String cmda : cmdAcl) {
					CmdAcl acl_tmp = CmdAcl.valueof(cmda);

					acl.add(acl_tmp);

					if (acl_tmp != CmdAcl.ADMIN) {
						nonAdminCommands = true;
					}
				}

				commandsACL.put(cmdId, acl);
			}
		}

		serviceEntity = new ServiceEntity(name, null, getDiscoDescription(), true);
		serviceEntity.addIdentities(new ServiceIdentity(getDiscoCategory(),
				getDiscoCategoryType(), getDiscoDescription()));
		serviceEntity.addFeatures("http://jabber.org/protocol/commands");

		CommandIfc command = new AddScriptCommand();

		command.init(CommandIfc.ADD_SCRIPT_CMD, "New command script");
		scriptCommands.put(command.getCommandId(), command);
		command = new RemoveScriptCommand();
		command.init(CommandIfc.DEL_SCRIPT_CMD, "Remove command script");
		scriptCommands.put(command.getCommandId(), command);
		if (props.get(SCRIPTS_DIR_PROP_KEY) != null) {
			scriptsBaseDir = (String) props.get(SCRIPTS_DIR_PROP_KEY);
			scriptsCompDir = scriptsBaseDir + "/" + getName();
			loadScripts();
		}
	}

	/**
	 * Method description
	 * 
	 * 
	 * @param manager
	 */
	@Override
	public void setVHostManager(VHostManagerIfc manager) {
		this.vHostManager = manager;
	}

	/**
	 * Method description
	 * 
	 * 
	 * @param jid
	 * @param node
	 * @param description
	 * @param admin
	 */
	public void updateServiceDiscoveryItem(String jid, String node, String description,
			boolean admin) {
		updateServiceDiscoveryItem(jid, node, description, admin, (String[]) null);
	}

	/**
	 * Method description
	 * 
	 * 
	 * @param jid
	 * @param node
	 * @param description
	 * @param admin
	 * @param features
	 */
	public void updateServiceDiscoveryItem(String jid, String node, String description,
			boolean admin, String... features) {
		updateServiceDiscoveryItem(jid, node, description, null, null, admin, features);
	}

	/**
	 * Method description
	 * 
	 * 
	 * @param jid
	 * @param node
	 * @param description
	 * @param category
	 * @param type
	 * @param admin
	 * @param features
	 */
	public void updateServiceDiscoveryItem(String jid, String node, String description,
			String category, String type, boolean admin, String... features) {
		if (serviceEntity.getJID().equals(jid) && (serviceEntity.getNode() == node)) {
			serviceEntity.setAdminOnly(admin);
			serviceEntity.setDescription(description);

			if ((category != null) || (type != null)) {
				serviceEntity.addIdentities(new ServiceIdentity(category, type, description));
			}

			if (features != null) {
				serviceEntity.setFeatures("http://jabber.org/protocol/commands");
				serviceEntity.addFeatures(features);
			}

			if (log.isLoggable(Level.FINEST)) {
				log.log(Level.FINEST, "Modifying service-discovery info: {0}", serviceEntity);
			}
		} else {
			ServiceEntity item = new ServiceEntity(jid, node, description, admin);

			if ((category != null) || (type != null)) {
				item.addIdentities(new ServiceIdentity(category, type, description));
			}

			if (features != null) {
				item.addFeatures(features);
			}

			if (log.isLoggable(Level.FINEST)) {
				log.log(Level.FINEST, "Adding new item: {0}", item);
			}

			serviceEntity.addItems(item);
		}
	}

	protected boolean processScriptCommand(Packet pc, Queue<Packet> results) {

		// TODO: test if this is right
		// It is not, the packet should actually have packetFrom set at all times
		// to ensure the error can be sent back to the original sender.
		// if ((pc.getStanzaFrom() == null) || (pc.getPacketFrom() != null)) {
		//
		// // The packet has not gone through session manager yet
		// return false;
		// }
		// This test is more correct as it says whether the packet went through
		// session manager checking.
		// TODO: test if commands still work for users from different XMPP servers
		// with the right permission set.
		if (pc.getPermissions() == Permissions.NONE) {
			return false;
		}

		Iq iqc = (Iq) pc;
		Command.Action action = Command.getAction(iqc);

		if (action == Command.Action.cancel) {
			Packet result = iqc.commandResult(Command.DataType.result);

			Command.addTextField(result, "Note", "Command canceled.");
			results.offer(result);

			return true;
		}

		String strCommand = iqc.getStrCommand();
		CommandIfc com = scriptCommands.get(strCommand);

		if ((strCommand != null) && (com != null)) {
			boolean admin = false;

			try {
				admin = canCallCommand(iqc.getStanzaFrom(), strCommand);

				if (admin) {
					if (log.isLoggable(Level.FINER)) {
						log.log(Level.FINER, "Processing admin command: {0}", pc);
					}

					Bindings binds = com.getBindings();

					if (binds == null) {
						binds = scriptEngineManager.getBindings();
					}

					// Bindings binds = scriptEngineManager.getBindings();
					initBindings(binds);
					com.runCommand(iqc, binds, results);
				} else {
					if (log.isLoggable(Level.FINER)) {
						log.log(Level.FINER, "Command rejected non-admin detected: {0}",
								pc.getStanzaFrom());
					}

					results.offer(Authorization.FORBIDDEN.getResponseMessage(pc,
							"Only Administrator can call the command.", true));
				}
			} catch (Exception e) {
				log.log(Level.WARNING, "Unknown admin command processing exception: " + pc, e);
			}

			return true;
		} else {
			if (log.isLoggable(Level.FINEST)) {
				log.log(Level.FINEST, "No such command: {0}, ignoring packet: {1}", new Object[] {
						strCommand, pc });
			}
		}

		return false;
	}

	private void loadScripts() {

		log.log(Level.CONFIG, "Loading admin scripts for component: {0}.",
				new Object[] { getName() });

		File file = null;
		AddScriptCommand addCommand = new AddScriptCommand();
		Bindings binds = scriptEngineManager.getBindings();

		initBindings(binds);

		String[] dirs = new String[] { scriptsBaseDir, scriptsCompDir };

		for (String scriptsPath : dirs) {
			log.log(Level.CONFIG, "{0}: Loading scripts from directory: {1}", new Object[] {
					getName(), scriptsPath });
			try {
				File adminDir = new File(scriptsPath);

				if ((adminDir != null) && adminDir.exists()) {
					for (File f : adminDir.listFiles()) {

						// Just regular files here....
						if (f.isFile() && !f.toString().endsWith("~")) {
							String cmdId = null;
							String cmdDescr = null;
							String comp = null;

							file = f;

							StringBuilder sb = new StringBuilder();
							BufferedReader buffr = new BufferedReader(new FileReader(file));
							String line = null;

							while ((line = buffr.readLine()) != null) {
								sb.append(line).append("\n");

								int idx = line.indexOf(CommandIfc.SCRIPT_DESCRIPTION);

								if (idx >= 0) {
									cmdDescr =
											line.substring(idx + CommandIfc.SCRIPT_DESCRIPTION.length()).trim();
								}

								idx = line.indexOf(CommandIfc.SCRIPT_ID);

								if (idx >= 0) {
									cmdId = line.substring(idx + CommandIfc.SCRIPT_ID.length()).trim();
								}

								idx = line.indexOf(CommandIfc.SCRIPT_COMPONENT);

								if (idx >= 0) {
									comp =
											line.substring(idx + CommandIfc.SCRIPT_COMPONENT.length()).trim();
								}
							}

							buffr.close();

							if ((cmdId == null) || (cmdDescr == null) || (comp == null)) {
								log.log(Level.WARNING,
										"Admin script found but it has no command ID or command"
												+ "description: " + "{0}", file);

								continue;
							}

							// What components should load the script....
							String[] comp_names = comp.split(",");
							boolean found = false;

							for (String cmp : comp_names) {
								found = getName().equals(cmp);

								if (found) {
									break;
								}
							}

							if (!found) {
								log.log(Level.CONFIG, "{0}: skipping admin script for component: {1}",
										new Object[] { getName(), comp });

								continue;
							}

							int idx = file.toString().lastIndexOf('.');
							String ext = file.toString().substring(idx + 1);

							addCommand.addAdminScript(cmdId, cmdDescr, sb.toString(), null, ext, binds);
							log.log(
									Level.CONFIG,
									"{0}: Loaded admin command from file: {1}, id: {2}, ext: {3}, descr: {4}",
									new Object[] { getName(), file, cmdId, ext, cmdDescr });
						}
					}
				} else {
					log.log(Level.CONFIG, "Admin scripts directory is missing: {0}, creating...",
							adminDir);
					try {
						adminDir.mkdirs();
					} catch (Exception e) {
						log.log(Level.WARNING,
								"Can't create scripts directory , read-only filesystem: " + file, e);
					}
				}
			} catch (Exception e) {
				log.log(Level.WARNING, "Can't load the admin script file: " + file, e);
			}
		}
	}
}
